// Visitor interface
class CarElementVisitor
{
    method CarElementVisitor() { }
    method visitWheel(wheel) { }
    method visitEngine(engine) { }
    method visitBody(body) { }
    method visitCar(car) { }
}

// Visited interface
class CarElement
{
    method CarElement() { }
    method accept(visitor) { } // CarElements have to provide accept().
}
 
class Wheel : CarElement
{
    var name_;
 
    method Wheel(name)
    {
        this.name_ = name;
    }
 
    method getName()
    {
        return this.name_;
    }
 
    method accept(visitor)
    {
        /*
         * accept(CarElementVisitor) in Wheel :
         * accept(CarElementVisitor) in CarElement, so the call
         * to accept is bound at run time. This can be considered
         * the first dispatch. However, the decision to call
         * visit(Wheel) (as opposed to visit(Engine) etc.) can be
         * made during compile time since 'this' is known at compile
         * time to be a Wheel. Moreover, each implementation of
         * CarElementVisitor : the visit(Wheel), which is
         * another decision that is made at run time. This can be
         * considered the second dispatch.
         */ 
        visitor.visitWheel(this);
    }
}
 
class Engine : CarElement
{
    method Engine() { }
    method accept(visitor)
    {
        visitor.visitEngine(this);
    }
}
 
class Body : CarElement
{
    method Body() { }
    method accept(visitor)
    {
        visitor.visitBody(this);
    }
}
 
class Car : CarElement
{
    var elements;
 
    method Car()
    {
        //create new Array of elements
        this.elements = array();
        array_push(this.elements, new Wheel("front left"));
        array_push(this.elements, new Wheel("front right"));
        array_push(this.elements, new Wheel("back left"));
        array_push(this.elements, new Wheel("back right"));
        array_push(this.elements, new Body());
        array_push(this.elements, new Engine());
    }
 
    method accept(visitor)
    {
        var n = array_size(this.elements);
        var i;
        for (i = 0; i < n; i = i + 1)
        {
            this.elements[i].accept(visitor);
        }
        visitor.visitCar(this);
    }
}
 
class CarElementPrintVisitor : CarElementVisitor
{
    method CarElementPrintVisitor() { }
    method visitWheel(wheel)
    {      
        println("Visiting ", wheel.getName(), " wheel");
    }
 
    method visitEngine(engine) {
        println("Visiting engine");
    }
 
    method visitBody(body) {
        println("Visiting body");
    }
 
    method visitCar(car) {      
        println("Visiting car");
    }
}
 
class CarElementDoVisitor : CarElementVisitor
{
    method CarElementDoVisitor() { }
    method visitWheel(wheel)
    {
        println("Kicking my ", wheel.getName(), " wheel");
    }
 
    method visitEngine(engine)
    {
        println("Starting my engine");
    }
 
    method visitBody(body)
    {
        println("Moving my body");
    }
 
    method visitCar(car)
    {
        println("Starting my car");
    }
}
 
function main()
{
    var car = new Car();
    car.accept(new CarElementPrintVisitor());
    car.accept(new CarElementDoVisitor());
}

